package xyz.itpath.tools;

import lombok.Data;
import xyz.itpath.tools.annotation.SummaryIgnore;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhaom
 * @date 2021/11/12 - 8:28
 * <p>
 * 汇总计算工具
 */
public class SummaryUtils {


    public static <E> void summaryListToA(Collection<?> list, E b) throws InvocationTargetException, IllegalAccessException {
        if (!list.isEmpty()) {
            Class<?> aClass = list.stream().findAny().get().getClass();
            List<Field> aFields = getComputableFields(aClass);
            Set<String> aNames = aFields.stream().map(Field::getName).collect(Collectors.toSet());
            List<Field> bFields = getComputableFields(b.getClass());
            // 求交集
            List<String> reSet = new ArrayList<>();
            bFields.forEach(item -> {
                String name = item.getName();
                if (aNames.contains(name)) {
                    reSet.add(name);
                }
            });

            // 开始汇总
            reSet.forEach(name -> {
                String upperFieldName = StringUtil.upperFirstLetter(name);
                BigDecimal value = doLoopSummary(list, "get" + upperFieldName, aClass);
                doLoopAssignment(b, value, name, b.getClass());
            });
        }
    }


    private static BigDecimal doLoopSummary(Collection<?> list, String methodName, Class<?> a) {
        BigDecimal result = BigDecimal.ZERO;

        try {
            Method method = a.getMethod(methodName);
            for (Object o : list) {
                BigDecimal v = (BigDecimal) method.invoke(o);
                if (v != null) {
                    result = result.add(v);
                }
            }
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            // 如果执行方法时报错，则尝试执行父类的方法，当遇到BaseModel时停止递归
            if ("BaseModel".equals(a.getSimpleName())) {
                return BigDecimal.ZERO;
            }
            return doLoopSummary(list, methodName, a.getSuperclass());
        }
        return result;
    }


    private static void doLoopAssignment(Object o, BigDecimal value, String fieldName, Class<?> a) {
        try {
            Field declaredField = a.getDeclaredField(fieldName);
            declaredField.setAccessible(true);
            declaredField.set(o,value);
            declaredField.setAccessible(false);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取BigDecimal属性
     */
    private static List<Field> getComputableFields(Class<?> clazz) {
        if ("BaseModel".equals(clazz.getSimpleName())) {
            return new ArrayList<>();
        }
        Field[] declaredFields = clazz.getDeclaredFields();
        List<Field> currentField = Arrays.stream(declaredFields)
                .filter(f -> "java.math.BigDecimal".equals(f.getGenericType().getTypeName()))
                .filter(f -> !f.isAnnotationPresent(SummaryIgnore.class))
                .collect(Collectors.toList());
        currentField.addAll(getComputableFields(clazz.getSuperclass()));
        return currentField;
    }

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        A a = new A();
        List<A> test = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            A b = new A();
            b.setA(BigDecimal.valueOf(i));
            b.setB(BigDecimal.valueOf(2 * i));
            test.add(b);
        }

        SummaryUtils.summaryListToA(test, a);

        System.out.println(a);
        System.out.println("-----------");
        test.forEach(System.out::println);
    }

    @Data
    static class A {
        private BigDecimal a;
        private BigDecimal b;
    }


}
